﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SnakeGame
{
    public class Snake
    {
        private const int DefaultBodyLength = 5;

        private LinkedList<SnakeBodyElement> body;
        private SnakeBodyElement head;
        private MatrixCoordinate currentDirection;

        public Snake(int headX, int headY, MatrixCoordinate startDirection, int snakeBodyLength = DefaultBodyLength)
        {
            this.InitializeSnake(headX, headY, snakeBodyLength);
            this.currentDirection = startDirection;
        }

        public SnakeBodyElement Head
        {
            get
            {
                return this.head;
            }

            set
            {
                this.head = value;
            }
        }

        public LinkedList<SnakeBodyElement> Body
        {
            get
            {
                return this.body;
            }
        }

        public MatrixCoordinate CurrentDirection
        {
            get
            {
                return this.currentDirection;
            }

            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("The current direction cannot be null.");
                }

                this.currentDirection = value;
            }
        }

        public SnakeBodyElement GetNextHead()
        {
            int nextHeadX = this.Head.X + this.CurrentDirection.X;
            int nextHeadY = this.Head.Y + this.CurrentDirection.Y;

            SnakeBodyElement nextHead = new SnakeBodyElement(nextHeadX, nextHeadY);

            return nextHead;
        }

        public void Move()
        {
            // The snake is a linked list. It's head is the last element, it's tail is the first element.

            SnakeBodyElement nextHead = this.GetNextHead();
            this.body.AddLast(nextHead); // Adding a new head. (New last element).
            this.body.RemoveFirst(); // Removing the tail.
            this.Head = nextHead;
        }

        public void Grow()
        {
            SnakeBodyElement snakeTail = this.body.First();
            int newBodyElementX = snakeTail.X - currentDirection.X;
            int newBodyElementY = snakeTail.Y - currentDirection.Y;
            SnakeBodyElement newBodyElement = new SnakeBodyElement(newBodyElementX, newBodyElementY);
            this.body.AddFirst(newBodyElement); // Adding to the tail of the snake.
        }

        public void TurnLeft()
        {
            if (currentDirection != Direction.Right)
            {
                currentDirection = Direction.Left;
            }
        }

        public void TurnRight()
        {
            if (currentDirection != Direction.Left)
            {
                currentDirection = Direction.Right;
            }
        }

        public void TurnUp()
        {
            if (currentDirection != Direction.Down)
            {
                currentDirection = Direction.Up;
            }
        }

        public void TurnDown()
        {
            if (currentDirection != Direction.Up)
            {
                currentDirection = Direction.Down;
            }
        }

        private void InitializeSnake(int headX, int headY, int snakeBodyLength)
        {
            this.body = new LinkedList<SnakeBodyElement>();

            for (int i = 0; i < snakeBodyLength; i++)
            {
                SnakeBodyElement bodyElement = new SnakeBodyElement(headX - i, headY);
                this.body.AddFirst(bodyElement);
            }

            this.head = this.body.Last();
        }
    }
}
